package com.zdp.chat.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zdp.chat.config.socket.SocketClient;
import com.zdp.chat.entity.chat.ChatMessageReq;
import com.zdp.chat.entity.resp.WSBaseResp;
import com.zdp.chat.handler.AbstractChatMessageHandler;
import com.zdp.chat.service.WebSocketService;
import com.zdp.chat.util.NettyUtil;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Objects;

@Slf4j
@RequiredArgsConstructor
@Component
public class WebSocketServiceImpl implements WebSocketService {

    /**
     * 所有在线的用户和对应的socket
     */
    private final SocketClient socketClient;

    private final List<AbstractChatMessageHandler<?,?,?>> handlers;


    @Override
    public void removed(Channel channel) {
        String uid = String.valueOf(NettyUtil.getAttr(channel, NettyUtil.UID));
        if (socketClient.containsKey(uid)
                && Objects.nonNull(socketClient.get(uid))
                && socketClient.get(uid).equals(channel)) {
            log.warn("客户端关闭, 客户端id:{}, uid:{}", channel.id(), uid);
            socketClient.get(uid).close();
            socketClient.remove(uid);
        }
    }

    /**
     * 通过心跳保存已连接客户端的channel
     * @param channel
     */
    @Override
    public void authorize(Channel channel) {
        //TODO 通过token获取用户id  Object loginId = StpUtil.getLoginIdByToken(StringUtils.substringAfterLast(token, " "));
        if (Objects.isNull(NettyUtil.getAttr(channel, NettyUtil.UID))) {
            this.sendMsg(channel, "ws请求未携带uid无法正常使用,已被强制断开,请检查你的请求地址");
            channel.close();
            return;
        }

        String uid = String.valueOf(NettyUtil.getAttr(channel, NettyUtil.UID));
        //TODO 判断当前是否允许重复登录,检查当前用户是否存在连接
        if (duplicateConnection(channel, uid)) {
            Channel old = socketClient.get(uid);
            this.sendMsg(old, "存在重复连接,您已被踢下线!~");
            // 关闭过期的连接避免重复连接导致内存泄露
            old.close();
        }
        socketClient.put(uid, channel);
        log.info("客户端id:{}, uid:{}, 心跳正常...", channel.id(), uid);
    }

    private boolean duplicateConnection(Channel channel, String uid) {
        return socketClient.containsKey(uid)
                && Objects.nonNull(socketClient.get(uid))
                && !(socketClient.get(uid).equals(channel));
    }

    /**
     * 给本地channel发送消息
     */
    private void sendMsg(Channel channel, WSBaseResp<?> wsBaseResp) {
        if (channel.isActive()) {
            channel.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(wsBaseResp)));
        }
    }

    private void sendMsg(Channel channel, String msg) {
        if (channel.isActive()) {
            channel.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(msg)));
        }
    }

    @Override
    public <T> void send(ChatMessageReq<T> req) {
        AbstractChatMessageHandler messageHandler = handlers.stream().filter(x -> x.apply(req.getMsgType())).findFirst().orElse(null);
        Assert.isTrue(Objects.nonNull(messageHandler), "暂不支持的消息类型");
        messageHandler.handler(req);
    }
}
